all repos — caroster @ 52ad6be503528baa3feff0e610b44f0f82ffc2a3

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Linkify from 'linkify-react';
  3import Tooltip from '@mui/material/Tooltip';
  4import IconButton from '@mui/material/IconButton';
  5import Box from '@mui/material/Box';
  6import Link from '@mui/material/Link';
  7import Card from '@mui/material/Card';
  8import Container from '@mui/material/Container';
  9import TextField from '@mui/material/TextField';
 10import Typography from '@mui/material/Typography';
 11import TuneIcon from '@mui/icons-material/Tune';
 12import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
 13import {useTheme} from '@mui/material/styles';
 14import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 15import {PropsWithChildren, useState} from 'react';
 16import {useTranslation} from 'react-i18next';
 17import pageUtils from '../../../lib/pageUtils';
 18import DetailsLink from '../../../containers/DetailsLink';
 19import ShareEvent from '../../../containers/ShareEvent';
 20import PlaceInput from '../../../containers/PlaceInput';
 21import LangSelector from '../../../components/LangSelector';
 22import usePermissions from '../../../hooks/usePermissions';
 23import useEventStore from '../../../stores/useEventStore';
 24import useToastStore from '../../../stores/useToastStore';
 25import EventLayout, {TabComponent} from '../../../layouts/Event';
 26import {
 27  EventByUuidDocument,
 28  useUpdateEventMutation,
 29} from '../../../generated/graphql';
 30
 31interface Props {
 32  eventUUID: string;
 33  announcement?: string;
 34}
 35
 36const Page = (props: PropsWithChildren<Props>) => {
 37  return <EventLayout {...props} Tab={DetailsTab} />;
 38};
 39
 40const DetailsTab: TabComponent<Props> = ({}) => {
 41  const {t} = useTranslation();
 42  const {
 43    userPermissions: {canEditEventDetails},
 44  } = usePermissions();
 45  const theme = useTheme();
 46  const [updateEvent] = useUpdateEventMutation();
 47  const addToast = useToastStore(s => s.addToast);
 48  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 49  const event = useEventStore(s => s.event);
 50  const [isEditing, setIsEditing] = useState(false);
 51
 52  if (!event) return null;
 53
 54  const onSave = async e => {
 55    try {
 56      const {uuid, ...data} = event;
 57      const {
 58        id,
 59        travels,
 60        waitingPassengers,
 61        __typename,
 62        administrators,
 63        passengers,
 64        ...input
 65      } = data;
 66      await updateEvent({
 67        variables: {
 68          uuid,
 69          eventUpdate: {
 70            ...input,
 71          },
 72        },
 73        refetchQueries: ['eventByUUID'],
 74      });
 75      setIsEditing(false);
 76    } catch (error) {
 77      console.error(error);
 78      addToast(t('event.errors.cant_update'));
 79    }
 80  };
 81
 82  const modifyButton = isEditing ? (
 83    <Tooltip
 84      title={t('event.details.save')}
 85      sx={{
 86        position: 'absolute',
 87        top: theme.spacing(2),
 88        right: theme.spacing(2),
 89      }}
 90    >
 91      <IconButton color="primary" onClick={onSave}>
 92        <CheckCircleOutlineIcon />
 93      </IconButton>
 94    </Tooltip>
 95  ) : (
 96    <Tooltip
 97      title={t('event.details.modify')}
 98      sx={{
 99        position: 'absolute',
100        top: theme.spacing(2),
101        right: theme.spacing(2),
102      }}
103    >
104      <IconButton color="primary" onClick={() => setIsEditing(true)}>
105        <TuneIcon />
106      </IconButton>
107    </Tooltip>
108  );
109
110  return (
111    <Box
112      sx={{
113        position: 'relative',
114      }}
115    >
116      <Container
117        sx={{
118          p: 4,
119          mt: 6,
120          mb: 11,
121          mx: 0,
122          [theme.breakpoints.down('md')]: {
123            p: 2,
124          },
125        }}
126      >
127        <Card
128          sx={{
129            position: 'relative',
130            maxWidth: '100%',
131            width: '480px',
132            p: 2,
133          }}
134        >
135          <Typography variant="h4" pb={2}>
136            {t('event.details')}
137          </Typography>
138          {canEditEventDetails() && modifyButton}
139          {(isEditing || event.name) && (
140            <Box pt={2} pr={1.5}>
141              <Typography variant="overline">
142                {t('event.fields.name')}
143              </Typography>
144              <Typography>
145                {isEditing ? (
146                  <TextField
147                    size="small"
148                    fullWidth
149                    value={event.name}
150                    onChange={e => setEventUpdate({name: e.target.value})}
151                    name="name"
152                    id="EditEventName"
153                  />
154                ) : (
155                  <Typography id="EventName">{event.name}</Typography>
156                )}
157              </Typography>
158            </Box>
159          )}
160          {(isEditing || event.address) && (
161            <Box pt={2} pr={1.5}>
162              <Typography variant="overline">
163                {t('event.fields.date')}
164              </Typography>
165              {isEditing ? (
166                <Typography>
167                  <DatePicker
168                    slotProps={{
169                      textField: {
170                        size: 'small',
171                        id: `EditEventDate`,
172                        fullWidth: true,
173                        placeholder: t('event.fields.date_placeholder'),
174                      },
175                    }}
176                    format="DD/MM/YYYY"
177                    value={moment(event.date)}
178                    onChange={date =>
179                      setEventUpdate({
180                        date: !date ? null : moment(date).format('YYYY-MM-DD'),
181                      })
182                    }
183                  />
184                </Typography>
185              ) : (
186                <Box position="relative">
187                  <Typography id="EventDate">
188                    {moment(event.date).format('DD/MM/YYYY')}
189                  </Typography>
190                </Box>
191              )}
192            </Box>
193          )}
194          {(isEditing || event.address) && (
195            <Box pt={2} pr={1.5}>
196              <Typography variant="overline">
197                {t('event.fields.address')}
198              </Typography>
199              {isEditing ? (
200                <PlaceInput
201                  place={event.address}
202                  latitude={event.latitude}
203                  longitude={event.longitude}
204                  onSelect={({place, latitude, longitude}) =>
205                    setEventUpdate({
206                      address: place,
207                      latitude,
208                      longitude,
209                    })
210                  }
211                />
212              ) : (
213                <Box position="relative">
214                  <Typography id="EventAddress" sx={{pr: 3}}>
215                    <Link
216                      target="_blank"
217                      rel="noreferrer"
218                      href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
219                        event.address
220                      )}`}
221                      onClick={e => e.preventDefault}
222                    >
223                      {event.address}
224                    </Link>
225                  </Typography>
226                </Box>
227              )}
228            </Box>
229          )}
230          {(isEditing || event.description) && (
231            <Box pt={2} pr={1.5}>
232              <Typography variant="overline">
233                {t('event.fields.description')}
234              </Typography>
235              {isEditing ? (
236                <Typography>
237                  <TextField
238                    fullWidth
239                    multiline
240                    maxRows={4}
241                    inputProps={{maxLength: 250}}
242                    value={event.description || ''}
243                    onChange={e =>
244                      setEventUpdate({description: e.target.value})
245                    }
246                    id={`EditEventDescription`}
247                    name="description"
248                  />
249                </Typography>
250              ) : (
251                <Typography
252                  id="EventDescription"
253                  sx={{pr: 3, whiteSpace: 'pre-line'}}
254                >
255                  <Linkify options={{render: DetailsLink}}>
256                    {event.description}
257                  </Linkify>
258                </Typography>
259              )}
260            </Box>
261          )}
262          {(isEditing || event.lang) && (
263            <Box pt={2} pr={1.5}>
264              <Typography variant="overline">
265                {t('event.fields.lang')}
266              </Typography>
267              {isEditing ? (
268                <LangSelector
269                  value={event.lang}
270                  onChange={lang => setEventUpdate({lang})}
271                />
272              ) : (
273                <Typography id="EventLang" sx={{pr: 3}}>
274                  {t(`PROTECTED.languages.${event.lang}`)}
275                </Typography>
276              )}
277            </Box>
278          )}
279          {!isEditing && (
280            <ShareEvent
281              title={`Caroster ${event.name}`}
282              sx={{width: '100%', mt: 2}}
283            />
284          )}
285        </Card>
286      </Container>
287    </Box>
288  );
289};
290
291export const getServerSideProps = pageUtils.getServerSideProps(
292  async (context, apolloClient) => {
293    const {uuid} = context.query;
294    const {host = ''} = context.req.headers;
295    let event = null;
296
297    // Fetch event
298    try {
299      const {data} = await apolloClient.query({
300        query: EventByUuidDocument,
301        variables: {uuid},
302      });
303      event = data?.eventByUUID?.data;
304    } catch (error) {
305      return {
306        notFound: true,
307      };
308    }
309
310    return {
311      props: {
312        eventUUID: uuid,
313        metas: {
314          title: event?.attributes?.name || '',
315          url: `https://${host}${context.resolvedUrl}`,
316        },
317      },
318    };
319  }
320);
321export default Page;